//@author A0101906R
#include "CmdAdd.h"

using namespace Twentyfifth;
using namespace std;

const std::string CmdAdd::FEEDBACK_ADDOK = "Task successfully added.\n";
const std::string CmdAdd::FEEDBACK_UNDOADDOK = "Undo add successful.\n";
const std::string CmdAdd::ERROR_INVALIDTASK = "Invalid task type, cannot be add.\n";
const std::string CmdAdd::ERROR_INVALIDSTART = "Invalid start date/time for task. \n";
const std::string CmdAdd::ERROR_INVALIDEND = "Invalid end date/time for task. \n";
const std::string CmdAdd::ERROR_INVALIDDEADLINE = "Invalid deadline for task. \n";
const std::string CmdAdd::ERROR_INVALIDSEQUENCESE = "End time should not be earlier than start time. \n";
const std::string CmdAdd::ERROR_INVALIDSEQUENCEDL = "Deadline has already passed. \n";
const std::string CmdAdd::ERROR_EMPTYDESC = "Task description should not be empty. \n";

//@author A0101906R
CmdAdd::CmdAdd() {
	cmdType = ADD;
}
//@author A0101906R
Status CmdAdd::execution() {

	Status addStatus;
	assert(storage != NULL);

	try {
		checkException();

		addStatus = storage->addTask(task);
		//to retrieve the task just added, for undo purpose.	
		rememberTask(addStatus);
		setDescForAdd(addStatus);

	}
	catch (string error) {
		addStatus.desc = error;
	}
	return addStatus;
}
//@author A0101906R
Status CmdAdd::undo(Status::StatusCode c) {
	Status undoStatus;
	assert(storage != NULL);
	try {
		if (c != Status::OK) {
			throw ERROR_UNSUCCESSFUL;
		}
		//undo add, ie. delete the task just added.
		undoStatus = storage->deleteTask(task.getId(), true);
		setDescForUndo(undoStatus);
	}
	catch (string error) {
		undoStatus.desc = error;
	}
	return undoStatus;
}
//@author A0101906R
void CmdAdd::checkException() {
	Task::TaskType taskType;
	DateTime start, end, deadline, current;

	try {
		taskType = task.getTaskType();
		if (taskType == Task::INVALID) {
			throw ERROR_INVALIDTASK;
		}
		if (task.getDescription().size() == 0) {
			throw ERROR_EMPTYDESC;
		}
		if (taskType == Task::TIMED_TASK) {
			start = task.getStart();
			end = task.getEnd();
			if (!start.isValidDate(start.getDay(), start.getMonth(), start.getYear())) {
				throw ERROR_INVALIDSTART;
			}
			if (!start.isValidTime(start.getHour(), start.getMinute(), start.getSecond())) {
				throw ERROR_INVALIDSTART;
			}
			if (!end.isValidDate(end.getDay(), end.getMonth(), end.getYear())) {
				throw ERROR_INVALIDEND;
			}
			if (!end.isValidTime(end.getHour(), end.getMinute(), end.getSecond())) {
				throw ERROR_INVALIDEND;
			}
			if (end <= start) {
				throw ERROR_INVALIDSEQUENCESE;
			}

		}
		if (taskType == Task::DEADLINE_TASK) {
			deadline = task.getDeadline();
			if (!deadline.isValidDate(deadline.getDay(), deadline.getMonth(), deadline.getYear())) {
				throw ERROR_INVALIDDEADLINE;
			}
			if (!deadline.isValidTime(deadline.getHour(), deadline.getMinute(), deadline.getSecond())) {
				throw ERROR_INVALIDDEADLINE;
			}
			current = getCurrentDateTime();
			if (deadline < current) {
				throw ERROR_INVALIDSEQUENCEDL;
			}
		}
	}

	catch (string error) {
		throw error;
	}

	return;
}
//@author A0101906R
void CmdAdd::rememberTask(Status& s) {

	if (s.code != Status::OK) {
		s.desc = statusCodeToString(s);
		throw s.desc;
	}

	if (s.code == Status::OK) {
		assert(s.tasks.size() != 0);

		taskAdded = s.tasks[0];
		task = taskAdded;
	}

	return;
}
//@author A0101906R
void CmdAdd::setDescForAdd(Status& s) {

	if (s.code == Status::OK) {
		s.desc = FEEDBACK_ADDOK + s.tasks[0].toString();
		s.tasks.clear();
	} else {
		s.desc = statusCodeToString(s);
	}
	return;
}
//@author A0101906R
void CmdAdd::setDescForUndo(Status& s) {

	if (s.code == Status::OK) {
		s.desc = FEEDBACK_UNDOADDOK;
	} else {
		s.desc = statusCodeToString(s);
	}
	return;
}
